package com.xncoding.pos.controller;

import com.xncoding.pos.common.model.ResultGenerator;
import com.xncoding.pos.common.model.ResultModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.WebAsyncTask;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;


// Spring Boot 提供的 WebAsyncTask 的异步编程 API
// 相比上问介绍的 @Async注解，WebAsyncTask提供更加健全的 超时处理 和 异常处理 支持
@RestController
@Slf4j
public class WebAsyncTaskController {

    @GetMapping("/exception")
    public WebAsyncTask<String> exceptionController() {
        log.info(Thread.currentThread().getName() + " 进入helloController方法");
        Callable<String> callable = new Callable<String>() {

            @Override
            public String call() throws Exception {
                log.info(Thread.currentThread().getName() + " 进入call方法");
                throw new TimeoutException("调用超时!");
            }
        };
        log.info(Thread.currentThread().getName() + " 从helloController方法返回");
        return new WebAsyncTask<>(5000, callable);
    }

    // 在Spring Boot中（Spring MVC）下请求默认都是同步的，一个请求过去到结束都是由一个线程负责的，
    // 很多时候为了能够提高吞吐量，需要将一些操作异步化，比如一些耗时的业务逻辑可以异步化。
    @GetMapping("/world")
    public WebAsyncTask<String> worldController(@RequestParam Integer sleepSeconds) {
        log.info(Thread.currentThread().getName() + " 进入helloController方法");

        // 3s钟没返回，则认为超时
        WebAsyncTask<String> webAsyncTask = new WebAsyncTask<>(3000, new Callable<String>() {

            @Override
            public String call() throws Exception {
                log.info(Thread.currentThread().getName() + " 从helloService方法返回");
                TimeUnit.SECONDS.sleep(sleepSeconds);

                return "ok";
            }
        });
        log.info(Thread.currentThread().getName() + " 从helloController方法返回");

        webAsyncTask.onCompletion(new Runnable() {

            @Override
            public void run() {
                log.info(Thread.currentThread().getName() + " 执行完毕");
            }
        });

        webAsyncTask.onTimeout(new Callable<String>() {
            @Override
            public String call() throws Exception {
                log.info(Thread.currentThread().getName() + " onTimeout");
                // 超时的时候，直接抛异常，让外层统一处理超时异常
                throw new TimeoutException("调用超时");
            }
        });
        return webAsyncTask;
    }


    @GetMapping("/completion")
    public WebAsyncTask<String> asyncTaskCompletion(@RequestParam Integer sleepSeconds,
                                                    @RequestParam Integer param) {
        // 打印处理线程名
        log.info("asyncTaskCompletion执行方法-----打印日志------{}", Thread.currentThread().getName());

        // 模拟开启一个异步任务，超时时间为5s
        WebAsyncTask<String> asyncTask = new WebAsyncTask<String>(5 * 1000L, () -> {
            log.info("异步工作线程-----打印日志------{}", Thread.currentThread().getName());

            if (param == 0) {
                throw new Exception("抛出异常");
            }

            // 任务处理时间3s，不超时
            TimeUnit.SECONDS.sleep(sleepSeconds);

            return "任务执行成功";
        });

        // 任务执行完成时调用该方法
        asyncTask.onCompletion(() -> {
            log.info("任务执行完成时调用该方法-----打印日志------{}", Thread.currentThread().getName());
        });

        asyncTask.onTimeout(() -> {
            log.warn("任务超时了-----打印日志------{}", Thread.currentThread().getName());
            return "任务超时了";
        });

        //当异步任务抛出异常的时候，onError()方法即会被调用。
        asyncTask.onError(() -> {
            log.error("任务执行异常-----打印日志------{}", Thread.currentThread().getName());
            return "任务执行异常";
        });
        return asyncTask;
    }


    /**
     * 查询
     */
    @GetMapping("/aysncTask")
    @ResponseStatus(HttpStatus.OK)
    public WebAsyncTask<ResultModel> aysncTask(@RequestParam Integer sleepSeconds) {
        log.info("aysncTask被调用-----打印日志------{}", Thread.currentThread().getName());
        Callable<ResultModel> callable = () -> {
            TimeUnit.SECONDS.sleep(sleepSeconds);
            log.info("aysncTask执行方法-----打印日志------{}", Thread.currentThread().getName());
            return ResultGenerator.genSuccessResult();
        };
        return new WebAsyncTask<ResultModel>(callable);
    }


}
